import { Injectable, NgZone } from '@angular/core'
import { ElectronService } from 'ngx-electron'
import { IpcRenderer } from 'electron'
import { Observable, Subscriber } from 'rxjs'
import { nanoid } from 'nanoid'
import { asyncChannels } from '../../../../app/asyncChannels'

@Injectable({
    providedIn: 'root'
})
export class ElectronHttpClient {
    private ipcRenderer: IpcRenderer

    constructor(electron: ElectronService, private zone: NgZone) {
        this.ipcRenderer = electron.ipcRenderer
    }

    public request(config): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpRequest
        const replyChannel = asyncChannels.httpRequestReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, config)
        })
    }

    public get(url: string, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpGet
        const replyChannel = asyncChannels.httpGetReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, config)
        })
    }

    public delete(url: string, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpDelete
        const replyChannel = asyncChannels.httpDeleteReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, config)
        })
    }

    public head(url: string, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpHead
        const replyChannel = asyncChannels.httpHeadReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, config)
        })
    }

    public post(url: string, data?, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpPost
        const replyChannel = asyncChannels.httpPostReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, data, config)
        })
    }

    public put(url: string, data?, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpPut
        const replyChannel = asyncChannels.httpPutReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, data, config)
        })
    }

    public patch(url: string, data?, config?): Observable<any> {
        const uid = nanoid()
        const channel = asyncChannels.httpPatch
        const replyChannel = asyncChannels.httpPatchReply
        return new Observable(subscriber => {
            this.listenOne(replyChannel, uid, subscriber)
            this.ipcRenderer.send(channel, uid, url, data, config)
        })
    }

    private listenOne<T>(channel: string, uid: string, subscriber: Subscriber<T>) {
        let listener
        this.ipcRenderer.on(channel, listener = async (event, rid, [resp, err]) => {
            if (rid === uid) {
                this.ipcRenderer.removeListener(channel, listener)
                this.zone.run(() => {
                    err ? subscriber.error(err) : subscriber.next(resp)
                    subscriber.complete()
                })
            }
        })
    }
}
